
#include "imtPublic.h"
#include "imtSocket.h"


namespace iMate {

	/**initialise socket system
	@return success to true
	*/
	bool Socket::initSocketSystem()
	{
#ifdef _iMate_Win
		WSADATA wsaData;
		if (0 != ::WSAStartup(MAKEWORD(2,2), &wsaData))
		{
			return false;
		}
		return true;
#else
		::signal(SIGPIPE, SIG_IGN);
		return true;
#endif
	}
	/**shut down the socket system
	*/
	void Socket::releaseSocketSystem()
	{
#ifdef _iMate_Win
		::WSACleanup();
#else
		::signal(SIGPIPE, SIG_DFL);
#endif
	}

	/**connect to a specail server
	@param server server address: a numeric host address string 
	  @par dotted-decimal IPv4 address or an IPv6 hex address
    @param port a numeric string
	@return success to true
	*/
	bool Socket::connect(const char* server, const char* port)
	{
		close();

		SOCKET socketHandle = INVALID_SOCKET;
		addrinfo *result = NULL, *ptr = NULL, hints;
		int iResult;

		::bzero(&hints, sizeof(hints));
		hints.ai_family = AF_UNSPEC;
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_protocol = IPPROTO_TCP;

		// Resolve the server address and port
		iResult = ::getaddrinfo(server, port, &hints, &result);
		if (iResult != 0)
			return false;

		// Attempt to connect to an address until one succeeds
		for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) 
		{
			// Create a SOCKET for connecting to server
			socketHandle = ::socket(ptr->ai_family, ptr->ai_socktype, 
				                  ptr->ai_protocol);
			if (socketHandle == INVALID_SOCKET)
				return false;

			// Connect to server.
			iResult = ::connect( socketHandle, ptr->ai_addr, (int)ptr->ai_addrlen);
			if (iResult == SOCKET_ERROR) 
			{
				::closesocket(socketHandle);
				socketHandle = INVALID_SOCKET;
				continue;
			}
			break;
		}

		::freeaddrinfo(result);

		if (socketHandle == INVALID_SOCKET)
			return false;

		mHandle = socketHandle;
		return true;
	} 

	/*@return success to true*/
	bool Socket::disconnect()
	{
		if (mHandle != INVALID_SOCKET)
		{
			::shutdown(mHandle, SD_BOTH);
		}
		return true;
	}

	/**send buffer to server without delay
	@param buffer data to be sent
	@param size size of data
	@return size of data sent actually
	  @Note this function will try best to send all data in buffer
	*/
	int Socket::send(const char* buffer, int size)
	{
		SOCKET skt = mHandle;
		int sendSize = 0;
		while (size > 0)
		{
			int result = ::send(skt, buffer +sendSize, size, 0);
			if (result == SOCKET_ERROR)
			{
#ifdef _iMate_Win
				DWORD error = ::WSAGetLastError();
				if (error != WSAEINPROGRESS && error != WSAEMSGSIZE)
					return sendSize;
#else
				return sendSize;
#endif
			}
			else
			{
				sendSize += result;
				size -= result;
			}
		}

		return sendSize;
	}

	/**receive data from server
	@param buffer buffer to hold the data from server if any
	@param size the capacity of the buffer passed in
	   @Note size only indicates the capacity of buffer, not means 
	   @par that we'll receive such length of data from server.
    @return size of data received actually from server
	*/
	int Socket::recv(char* buffer, int size)
	{
		int receiveSize = ::recv(mHandle, buffer, size, 0);
		if (receiveSize == 0)
			return SOCKET_ERROR;
		if (receiveSize == SOCKET_ERROR)
		{
#ifdef _iMate_Win
			DWORD error = ::WSAGetLastError();
			if (error == WSAEINPROGRESS || error == WSAEMSGSIZE)
				return 0;
			return SOCKET_ERROR;
#else
			return SOCKET_ERROR;
#endif
		}
		return receiveSize;
	}

	///just close the socket
	void Socket::close()
	{
		if (mHandle != INVALID_SOCKET)
		{
			::closesocket(mHandle);
			mHandle = INVALID_SOCKET;
		}
	}

}//iMate